/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <pthread.h>
#include "sle_ssap_client_hdi.h"
#include "sle_ssap_config.h"
#include "sle_ssap_client_config.h"
#include "sle_hal_errcode.h"
#include "sle_hilog.h"
#include "sle_ssap_client.h"
#include "sle_callback_adapter.h"
#include <time.h>
#include <sched.h>
extern uint16_t SLE_CONNECT_UPDATE_INTERVAL_HDI;
extern uint8_t gle_tx_acb_data_num_get(void);
pthread_mutex_t send_mutex_client;

// 返回微秒
uint64_t sle_clock_gettime_us_kh_lite(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ((uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL);
}

SleHdiErrorCode SleHdiAdapterSsapcRegisterClient(const SleUuid *appUuid, uint8_t *clientId)
{
    SLE_ASSERT_AND_RETURN_RET(appUuid != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    SLE_ASSERT_AND_RETURN_RET(clientId != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ret = ssapc_register_client((sle_uuid_t*) appUuid, clientId);
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcUnregisterClient(uint8_t clientId)
{
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ret = ssapc_unregister_client(clientId);
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcFindStructure(uint8_t clientId, uint16_t connectId, const ClientFindStructure *param)
{
    SLE_ASSERT_AND_RETURN_RET(param != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ssapc_find_structure_param_t param_sdk = {
        .type = (uint8_t)param->findType,
        .start_hdl = param->startHandle,
        .end_hdl = param->endHandle,
        .reserve = param->reserve,
    };
    memcpy(&(param_sdk.uuid), &(param->uuid), sizeof(sle_uuid_t));
    ret = ssapc_find_structure(clientId, connectId, &param_sdk);
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcReadReqByUuid(uint8_t clientId, uint16_t connectId, const ClientReadReqByUUID *param)
{
    SLE_ASSERT_AND_RETURN_RET(param != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ssapc_read_req_by_uuid_param_t param_sdk = {
        .type = (uint8_t)param->findType,
        .start_hdl = param->startHandle,
        .end_hdl = param->endHandle,
    };
    memcpy(&(param_sdk.uuid), &(param->uuid), sizeof(sle_uuid_t));
    ret = ssapc_read_req_by_uuid(clientId, connectId, &param_sdk);
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcReadReq(uint8_t clientId, uint16_t connectId, uint16_t handle, uint8_t type)
{
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ret = ssapc_read_req(clientId, connectId, handle, type);
    return ret;
}

// SLE_CONNECT_UPDATE_INTERVAL_HDI = 18, 连接间隔
// 该接口严格在1个连接间隔只能发送一个包(修复上面接口的BUG, 一定要等待连接间隔的整数倍), 丢包率1%以内, 单包时延2ms,即1个连接间隔
static SleHdiErrorCode sle_write_sync(uint8_t clientId, uint16_t connectId, ssapc_write_param_t *param, bool isReq)
{
    SleHdiErrorCode ret = -1;
    int sflag = 0;
    uint64_t kht_before_get_jiffies = 0, kht_after_get_jiffies = 0;
    uint64_t after_us = 0;
    uint32_t wait_min_time_us = (SLE_CONNECT_UPDATE_INTERVAL_HDI * 125);  // 最少等待1个发送间隔(可以是连接间隔)
    int retry = 2000; // 最多等待2000个连接间隔时间
    kht_before_get_jiffies = sle_clock_gettime_us_kh_lite();
    after_us = kht_before_get_jiffies + wait_min_time_us; // 防止溢出回卷, 等待超时时间

    while (1) {
        if (sflag == 0 && gle_tx_acb_data_num_get() > 0) {
            if (isReq) {
                ret = ssapc_write_req(clientId, connectId, param);
            } else {
                ret = ssapc_write_cmd(clientId, connectId, param);
            }
            sflag = 1;
        }

        kht_after_get_jiffies = sle_clock_gettime_us_kh_lite();
        if ((int)(kht_after_get_jiffies - after_us) < 0) { // 这里至少等待1个连接间隔(或发送间隔)
            sched_yield();
        } else {
            if (sflag == 0 && (--retry) > 0) {
                after_us += wait_min_time_us; // 没有发出去包的话 这里要继续等待下一个连接间隔(或发送间隔)
                sched_yield();
                continue;
            }
            // 超过最大等待时间,没有发送出去, 失败退出
            // 或者发送出去了, 成功返回
            break;
        }
    }
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcWriteReq(uint8_t clientId, uint16_t connectId, const ClientWriteParam *param)
{
    SLE_ASSERT_AND_RETURN_RET(param != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    pthread_mutex_lock(&send_mutex_client);
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ssapc_write_param_t param_sdk = {
        .handle = param->handle,
        .type = (uint8_t)param->type,
        .data_len =param-> dataLength,
        .data = param->data,
    };
    ret = sle_write_sync(clientId, connectId, &param_sdk, true);
    pthread_mutex_unlock(&send_mutex_client);
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcWriteCmd(uint8_t clientId, uint16_t connectId, const ClientWriteParam *param)
{
    SLE_ASSERT_AND_RETURN_RET(param != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    pthread_mutex_lock(&send_mutex_client);
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ssapc_write_param_t param_sdk = {
        .handle = param->handle,
        .type = (uint8_t)param->type,
        .data_len =param-> dataLength,
        .data = param->data,
    };
    ret = sle_write_sync(clientId, connectId, &param_sdk, false);
    pthread_mutex_unlock(&send_mutex_client);
    return ret;
}

SleHdiErrorCode SleHdiAdapterSsapcExchangeInfoReq(uint8_t clientId, uint16_t connectId, const SsapMtuInfo* param)
{
    SLE_ASSERT_AND_RETURN_RET(param != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ret = ssapc_exchange_info_req(clientId, connectId, (ssap_exchange_info_t*)param);
    return ret;
}

SleHdiErrorCode SleHdiAdapterRegisterSsapcCallbacks(SleSsapcHdiCallbacks *ssapcCallbacks)
{
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ret = RegisterSsapcCallbackAdapter(ssapcCallbacks);
    return ret;
}

SleHdiErrorCode GetSleSsapcHdiOpts(SleSsapcHdiOpts *ssapcOpts)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(ssapcOpts != NULL, SLE_HAL_ERR_INVALID_PARAM, "GetSleSsapcHdiOpts");
    ssapcOpts->registerClient = SleHdiAdapterSsapcRegisterClient;
    ssapcOpts->unregisterClient = SleHdiAdapterSsapcUnregisterClient;
    ssapcOpts->findStructure = SleHdiAdapterSsapcFindStructure;
    ssapcOpts->readReqByUuid = SleHdiAdapterSsapcReadReqByUuid;
    ssapcOpts->readReq = SleHdiAdapterSsapcReadReq;
    ssapcOpts->writeReq = SleHdiAdapterSsapcWriteReq;
    ssapcOpts->writeCmd = SleHdiAdapterSsapcWriteCmd;
    ssapcOpts->exchangeInfoReq = SleHdiAdapterSsapcExchangeInfoReq;
    ssapcOpts->registerClientCbks = SleHdiAdapterRegisterSsapcCallbacks;
    return SLE_HDI_ERRCODE_SUCC;
}